@page "/TextDiff"
@inject IJSRuntime JS
<PageTitle>文本比对工具</PageTitle>

<div class="text-diff-container">
    <h1>文本比对工具</h1>
    
    <!-- 操作按钮区域 -->
    <div class="action-buttons">
        <button class="btn btn-primary" @onclick="CompareText">比对差异</button>
        <button class="btn btn-success" @onclick="MergeToLeft" disabled="@(!HasDifferences)">合并到左侧</button>
        <button class="btn btn-success" @onclick="MergeToRight" disabled="@(!HasDifferences)">合并到右侧</button>
        <button class="btn btn-warning" @onclick="UnlockEditing" disabled="@(!IsLocked)">解锁编辑</button>
        <button class="btn btn-info" @onclick="ClearAll">清空所有</button>
        <label style="display:flex;align-items:center;gap:6px;margin-left:auto;">
            <input type="checkbox" checked="@SyncScrollEnabled" @onchange="OnSyncScrollChanged" disabled="@(!IsLocked)" /> 同步滚动
        </label>
    </div>

    <!-- 文本输入区域 -->
    <div class="diff-inputs">
        <div class="diff-section">
            <label for="leftText">左侧文本</label>
            <div class="textarea-container">
                <div class="diff-textarea-wrapper">
                    <div class="diff-textarea-content" id="leftTextContent"></div>
                    <textarea id="leftText" @bind="LeftText" @oninput="OnLeftTextChanged" class="diff-textarea" placeholder="输入左侧文本..." disabled="@IsLocked"></textarea>
                </div>
            </div>
        </div>
        <div class="diff-section">
            <label for="rightText">右侧文本</label>
            <div class="textarea-container">
                <div class="diff-textarea-wrapper">
                    <div class="diff-textarea-content" id="rightTextContent"></div>
                    <textarea id="rightText" @bind="RightText" @oninput="OnRightTextChanged" class="diff-textarea" placeholder="输入右侧文本..." disabled="@IsLocked"></textarea>
                </div>
            </div>
        </div>
    </div>

    <!-- 差异统计信息 -->
    <div class="diff-stats" id="diffStats">
        @if (HasDifferences)
        {
            <div class="stats-item">
                <span class="stat-label">差异数量:</span>
                <span class="stat-value">@DiffCount</span>
            </div>
            <div class="stats-item">
                <span class="stat-label">左侧差异:</span>
                <span class="stat-value deleted">@LeftDiffCount</span>
            </div>
            <div class="stats-item">
                <span class="stat-label">右侧差异:</span>
                <span class="stat-value added">@RightDiffCount</span>
            </div>
        }
    </div>

    <!-- 历史记录区域 -->
    <div class="history-section">
        <h3>操作历史</h3>
        <div class="history-list" id="historyList">
            @if (HistoryRecords.Any())
            {
                @foreach (var record in HistoryRecords.Take(10))
                {
                    <div class="history-item">
                        <div class="history-time">@record.Timestamp.ToString("HH:mm:ss")</div>
                        <div class="history-action">@record.Action</div>
                        <div class="history-details">@record.Details</div>
                    </div>
                }
            }
            else
            {
                <div class="no-history">暂无操作记录</div>
            }
        </div>
    </div>
</div>

@code {
    private string LeftText { get; set; } = "";
    private string RightText { get; set; } = "";
    private string DiffHtml { get; set; } = string.Empty;
    private ElementReference diffResultDiv;
    
    // 差异相关属性
    private bool HasDifferences { get; set; } = false;
    private int DiffCount { get; set; } = 0;
    private int LeftDiffCount { get; set; } = 0;
    private int RightDiffCount { get; set; } = 0;
    private bool IsLocked { get; set; } = false; // 比对后锁定输入框
    private bool SyncScrollEnabled { get; set; } = true; // 同步滚动开关
    
    // 历史记录
    private List<HistoryRecord> HistoryRecords { get; set; } = new List<HistoryRecord>();
    
    public class HistoryRecord
    {
        public DateTime Timestamp { get; set; }
        public string Action { get; set; } = "";
        public string Details { get; set; } = "";
    }

    private async Task OnLeftTextChanged(ChangeEventArgs e)
    {
        LeftText = e.Value?.ToString() ?? "";
        await Task.CompletedTask;
    }

    private async Task OnRightTextChanged(ChangeEventArgs e)
    {
        RightText = e.Value?.ToString() ?? "";
        await Task.CompletedTask;
    }

    private async Task CompareText()
    {
        if (string.IsNullOrEmpty(LeftText) && string.IsNullOrEmpty(RightText))
        {
            await AddHistoryRecord("比对差异", "两个文本都为空");
            return;
        }

        var result = await JS.InvokeAsync<DiffResult>("renderLineDiffContents", LeftText, RightText);
        
        HasDifferences = result.HasDifferences;
        DiffCount = result.DiffCount;
        LeftDiffCount = result.LeftDiffCount;
        RightDiffCount = result.RightDiffCount;
        IsLocked = true; // 比对后锁定输入
        await JS.InvokeVoidAsync("setCrossSyncEnabled", SyncScrollEnabled);
        
        await AddHistoryRecord("比对差异", $"发现 {DiffCount} 处差异");
        StateHasChanged();
    }

    private async Task MergeToLeft()
    {
        if (!HasDifferences) return;
        
        var result = await JS.InvokeAsync<string>("mergeTextToLeft", LeftText, RightText);
        LeftText = result;
        
        await AddHistoryRecord("合并到左侧", "已将右侧差异合并到左侧文本");
        await CompareText(); // 重新比对
    }

    private async Task MergeToRight()
    {
        if (!HasDifferences) return;
        
        var result = await JS.InvokeAsync<string>("mergeTextToRight", LeftText, RightText);
        RightText = result;
        
        await AddHistoryRecord("合并到右侧", "已将左侧差异合并到右侧文本");
        await CompareText(); // 重新比对
    }

    private async Task ClearAll()
    {
        LeftText = "";
        RightText = "";
        HasDifferences = false;
        DiffCount = 0;
        LeftDiffCount = 0;
        RightDiffCount = 0;
        IsLocked = false; // 清空后解除锁定
        
        await JS.InvokeVoidAsync("clearAllHighlights");
        await JS.InvokeVoidAsync("setCrossSyncEnabled", false);
        await AddHistoryRecord("清空所有", "已清空所有文本和差异标记");
        StateHasChanged();
    }

    private async Task UnlockEditing()
    {
        // 不修改文本，仅解除锁定并清除高亮及统计
        IsLocked = false;
        HasDifferences = false;
        DiffCount = 0;
        LeftDiffCount = 0;
        RightDiffCount = 0;
        await JS.InvokeVoidAsync("clearAllHighlights");
        await JS.InvokeVoidAsync("setCrossSyncEnabled", false);
        await AddHistoryRecord("解锁编辑", "已清除高亮并恢复可编辑状态");
        StateHasChanged();
    }

    private async Task OnSyncScrollChanged(ChangeEventArgs e)
    {
        SyncScrollEnabled = e?.Value is bool b ? b : SyncScrollEnabled;
        await JS.InvokeVoidAsync("setCrossSyncEnabled", SyncScrollEnabled);
        await AddHistoryRecord("同步滚动", SyncScrollEnabled ? "已开启" : "已关闭");
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            // 初始为可编辑，无需跨区域同步
            await JS.InvokeVoidAsync("setCrossSyncEnabled", false);
        }
        await base.OnAfterRenderAsync(firstRender);
    }

    private async Task AddHistoryRecord(string action, string details)
    {
        HistoryRecords.Insert(0, new HistoryRecord
        {
            Timestamp = DateTime.Now,
            Action = action,
            Details = details
        });
        
        // 保持最多20条记录
        if (HistoryRecords.Count > 20)
        {
            HistoryRecords = HistoryRecords.Take(20).ToList();
        }
        
        await Task.CompletedTask;
    }

    public class DiffResult
    {
        public bool HasDifferences { get; set; }
        public int DiffCount { get; set; }
        public int LeftDiffCount { get; set; }
        public int RightDiffCount { get; set; }
        public List<HighlightRange> LeftHighlights { get; set; } = new List<HighlightRange>();
        public List<HighlightRange> RightHighlights { get; set; } = new List<HighlightRange>();
    }

    public class HighlightRange
    {
        public int Start { get; set; }
        public int Length { get; set; }
        public string Type { get; set; } = ""; // "added", "deleted", "modified"
    }
}

<script src="/lib/google-diff-match-patch/diff_match_patch.js"></script>
<script>
    // 高级文本比对功能
    window.compareTextDiffAdvanced = (left, right) => {
        if (!window.diff_match_patch) {
            return {
                hasDifferences: false,
                diffCount: 0,
                leftDiffCount: 0,
                rightDiffCount: 0,
                leftHighlights: [],
                rightHighlights: []
            };
        }
        
        var dmp = new diff_match_patch();
        var diffs = dmp.diff_main(left, right, false);
        dmp.diff_cleanupSemantic(diffs);
        
        var leftHighlights = [];
        var rightHighlights = [];
        var diffCount = 0;
        var leftDiffCount = 0;
        var rightDiffCount = 0;
        
        var leftPos = 0;
        var rightPos = 0;
        
        for (var i = 0; i < diffs.length; i++) {
            var diff = diffs[i];
            var op = diff[0]; // -1: delete, 0: equal, 1: insert
            var text = diff[1];
            
            if (op === -1) { // 删除 - 在左侧标记为红色
                leftHighlights.push({
                    start: leftPos,
                    length: text.length,
                    type: 'deleted'
                });
                leftPos += text.length;
                diffCount++;
                leftDiffCount++;
            } else if (op === 1) { // 插入 - 在右侧标记为绿色
                rightHighlights.push({
                    start: rightPos,
                    length: text.length,
                    type: 'added'
                });
                rightPos += text.length;
                diffCount++;
                rightDiffCount++;
            } else { // 相等
                leftPos += text.length;
                rightPos += text.length;
            }
        }
        
        return {
            hasDifferences: diffCount > 0,
            diffCount: diffCount,
            leftDiffCount: leftDiffCount,
            rightDiffCount: rightDiffCount,
            leftHighlights: leftHighlights,
            rightHighlights: rightHighlights
        };
    };
    
    // 应用文本高亮
    window.applyTextHighlighting = (containerId, highlights) => {
        var contentId = containerId === 'leftHighlight' ? 'leftTextContent' : 'rightTextContent';
        var textareaId = containerId === 'leftHighlight' ? 'leftText' : 'rightText';
        
        var contentDiv = document.getElementById(contentId);
        var textarea = document.getElementById(textareaId);
        
        if (!contentDiv || !textarea) return;
        
        var text = textarea.value;
        if (!text) {
            contentDiv.innerHTML = '';
            return;
        }
        
        // 创建带高亮的HTML内容
        var htmlContent = createHighlightedHTML(text, highlights);
        contentDiv.innerHTML = htmlContent;
        
        // 同步滚动
        syncScroll(textarea, contentDiv);
    };
    
    // 创建带高亮的HTML内容
    function createHighlightedHTML(text, highlights) {
        if (!highlights || highlights.length === 0) {
            return escapeHtml(text).replace(/\n/g, '<br>');
        }
        
        var result = '';
        var lastIndex = 0;
        
        // 按位置排序高亮
        highlights.sort(function(a, b) { return a.start - b.start; });
        
        highlights.forEach(function(highlight) {
            // 添加高亮前的内容
            if (highlight.start > lastIndex) {
                result += escapeHtml(text.substring(lastIndex, highlight.start)).replace(/\n/g, '<br>');
            }
            
            // 添加高亮内容
            var highlightText = text.substring(highlight.start, highlight.start + highlight.length);
            var escapedText = escapeHtml(highlightText).replace(/\n/g, '<br>');
            result += '<span class="diff-highlight diff-' + highlight.type + '">' + escapedText + '</span>';
            
            lastIndex = highlight.start + highlight.length;
        });
        
        // 添加剩余内容
        if (lastIndex < text.length) {
            result += escapeHtml(text.substring(lastIndex)).replace(/\n/g, '<br>');
        }
        
        return result;
    }
    
    // HTML转义
    function escapeHtml(text) {
        var div = document.createElement('div');
        div.textContent = text;
        return div.innerHTML;
    }
    
    // 同步滚动
    function syncScroll(textarea, contentDiv) {
        textarea.addEventListener('scroll', function() {
            contentDiv.scrollTop = textarea.scrollTop;
            contentDiv.scrollLeft = textarea.scrollLeft;
        });
    }
    
    // 计算高亮位置
    function calculateHighlightPosition(textarea, start, length) {
        var text = textarea.value;
        var lines = text.substring(0, start).split('\n');
        var highlightLines = text.substring(start, start + length).split('\n');
        
        var startLine = lines.length - 1;
        var startColumn = lines[lines.length - 1].length;
        
        var computedStyle = window.getComputedStyle(textarea);
        var lineHeight = parseInt(computedStyle.lineHeight) || 20;
        var paddingTop = parseInt(computedStyle.paddingTop) || 12;
        var paddingLeft = parseInt(computedStyle.paddingLeft) || 12;
        var fontSize = parseInt(computedStyle.fontSize) || 14;
        var charWidth = fontSize * 0.6; // 近似字符宽度
        
        var x = paddingLeft + (startColumn * charWidth);
        var y = paddingTop + (startLine * lineHeight);
        
        // 计算宽度和高度
        var width = charWidth * highlightLines[0].length;
        var height = lineHeight * highlightLines.length;
        
        return { x: x, y: y, width: width, height: height };
    }
    
    // 合并到左侧
    window.mergeTextToLeft = (left, right) => {
        if (!window.diff_match_patch) return left;
        
        var dmp = new diff_match_patch();
        var diffs = dmp.diff_main(left, right, false);
        dmp.diff_cleanupSemantic(diffs);
        
        var result = '';
        for (var i = 0; i < diffs.length; i++) {
            var diff = diffs[i];
            var op = diff[0];
            var text = diff[1];
            
            if (op !== -1) { // 保留相等和插入的内容
                result += text;
            }
        }
        
        return result;
    };
    
    // 合并到右侧
    window.mergeTextToRight = (left, right) => {
        if (!window.diff_match_patch) return right;
        
        var dmp = new diff_match_patch();
        var diffs = dmp.diff_main(left, right, false);
        dmp.diff_cleanupSemantic(diffs);
        
        var result = '';
        for (var i = 0; i < diffs.length; i++) {
            var diff = diffs[i];
            var op = diff[0];
            var text = diff[1];
            
            if (op !== 1) { // 保留相等和删除的内容
                result += text;
            }
        }
        
        return result;
    };
    
    // 清空所有高亮
    window.clearAllHighlights = () => {
        var leftContent = document.getElementById('leftTextContent');
        var rightContent = document.getElementById('rightTextContent');
        
        if (leftContent) leftContent.innerHTML = '';
        if (rightContent) rightContent.innerHTML = '';
    };

    // 行级比对并直接渲染到覆盖层，返回统计信息
    window.renderLineDiffContents = (left, right) => {
        var leftContent = document.getElementById('leftTextContent');
        var rightContent = document.getElementById('rightTextContent');
        var leftTextarea = document.getElementById('leftText');
        var rightTextarea = document.getElementById('rightText');

        if (!leftContent || !rightContent || !leftTextarea || !rightTextarea) {
            return { hasDifferences: false, diffCount: 0, leftDiffCount: 0, rightDiffCount: 0, leftHighlights: [], rightHighlights: [] };
        }

        var leftLines = (left || '').split('\n');
        var rightLines = (right || '').split('\n');

        if (!window.diff_match_patch) {
            // 无库时仅按简单差异标记
            var simple = simpleLineDiff(leftLines, rightLines);
            leftContent.innerHTML = simple.leftHTML;
            rightContent.innerHTML = simple.rightHTML;
            ensureSyncScroll(leftTextarea, leftContent);
            ensureSyncScroll(rightTextarea, rightContent);
            return { hasDifferences: simple.diffCount > 0, diffCount: simple.diffCount, leftDiffCount: simple.leftCount, rightDiffCount: simple.rightCount, leftHighlights: [], rightHighlights: [] };
        }

        var dmp = new diff_match_patch();
        // 使用内置的行到字符压缩来进行行级diff
        var a = dmp.diff_linesToChars_(left, right);
        var chars1 = a.chars1;
        var chars2 = a.chars2;
        var lineArray = a.lineArray;
        var diffs = dmp.diff_main(chars1, chars2, false);
        dmp.diff_cleanupSemantic(diffs);
        dmp.diff_charsToLines_(diffs, lineArray);

        var leftMarked = new Set();
        var rightMarked = new Set();
        var leftIdx = 0;
        var rightIdx = 0;
        var leftCount = 0;
        var rightCount = 0;

        // 记录改动块，用于行内配对
        var changeBlocks = [];
        var currentDel = [];
        var currentIns = [];

        function flushBlock() {
            if (currentDel.length > 0 || currentIns.length > 0) {
                changeBlocks.push({ deletions: currentDel.slice(), insertions: currentIns.slice() });
                currentDel = [];
                currentIns = [];
            }
        }

        for (var i = 0; i < diffs.length; i++) {
            var op = diffs[i][0];
            var text = diffs[i][1];
            var lines = text.split('\n');
            if (lines.length > 0 && lines[lines.length - 1] === '') { lines.pop(); }

            if (op === -1) { // 删除 -> 左侧高亮
                for (var j = 0; j < lines.length; j++) {
                    leftMarked.add(leftIdx + j);
                    currentDel.push({ leftLineIndex: leftIdx + j, text: lines[j] });
                }
                leftIdx += lines.length;
                leftCount += lines.length;
            } else if (op === 1) { // 插入 -> 右侧高亮
                for (var k = 0; k < lines.length; k++) {
                    rightMarked.add(rightIdx + k);
                    currentIns.push({ rightLineIndex: rightIdx + k, text: lines[k] });
                }
                rightIdx += lines.length;
                rightCount += lines.length;
            } else { // 相等
                // 相等块切断改动分组
                flushBlock();
                leftIdx += lines.length;
                rightIdx += lines.length;
            }
        }
        flushBlock();

        // 建立行对行的配对映射，用于行内高亮
        var leftToRightPair = new Map();
        var rightToLeftPair = new Map();
        changeBlocks.forEach(function(block){
            var n = Math.min(block.deletions.length, block.insertions.length);
            for (var i = 0; i < n; i++) {
                var li = block.deletions[i].leftLineIndex;
                var ri = block.insertions[i].rightLineIndex;
                leftToRightPair.set(li, ri);
                rightToLeftPair.set(ri, li);
            }
        });

        // 生成左侧HTML
        var leftHTMLParts = [];
        for (var li = 0; li < leftLines.length; li++) {
            var original = leftLines[li];
            if (leftMarked.has(li)) {
                var pairedIdx = leftToRightPair.has(li) ? leftToRightPair.get(li) : null;
                var innerHTML = pairedIdx !== null ? renderIntralineDiff(original, rightLines[pairedIdx], 'left') : escapeHtml(original);
                leftHTMLParts.push('<span class="diff-highlight diff-deleted">' + innerHTML + '</span>');
            } else {
                leftHTMLParts.push(escapeHtml(original));
            }
        }
        var leftHTML = leftHTMLParts.join('<br>');

        // 生成右侧HTML
        var rightHTMLParts = [];
        for (var ri = 0; ri < rightLines.length; ri++) {
            var originalR = rightLines[ri];
            if (rightMarked.has(ri)) {
                var pairedIdxL = rightToLeftPair.has(ri) ? rightToLeftPair.get(ri) : null;
                var innerHTMLR = pairedIdxL !== null ? renderIntralineDiff(leftLines[pairedIdxL], originalR, 'right') : escapeHtml(originalR);
                rightHTMLParts.push('<span class="diff-highlight diff-added">' + innerHTMLR + '</span>');
            } else {
                rightHTMLParts.push(escapeHtml(originalR));
            }
        }
        var rightHTML = rightHTMLParts.join('<br>');

        leftContent.innerHTML = leftHTML;
        rightContent.innerHTML = rightHTML;

        ensureSyncScroll(leftTextarea, leftContent);
        ensureSyncScroll(rightTextarea, rightContent);
        // 确保跨区域同步已绑定
        bindCrossSync();

        var diffCount = leftCount + rightCount;
        return { hasDifferences: diffCount > 0, diffCount: diffCount, leftDiffCount: leftCount, rightDiffCount: rightCount, leftHighlights: [], rightHighlights: [] };
    };

    // 行内字符级高亮：left 侧突出 deletions，right 侧突出 insertions
    function renderIntralineDiff(leftLine, rightLine, side) {
        var dmp = new diff_match_patch();
        var diffs = dmp.diff_main(leftLine || '', rightLine || '', false);
        dmp.diff_cleanupSemantic(diffs);
        var parts = [];
        for (var i = 0; i < diffs.length; i++) {
            var op = diffs[i][0];
            var text = escapeHtml(diffs[i][1]);
            if (op === 0) {
                parts.push(text);
            } else if (op === -1) { // 删除
                if (side === 'left') {
                    parts.push('<span class="diff-token diff-token-deleted">' + text + '</span>');
                }
                // 右侧对删除文本不显示
            } else if (op === 1) { // 插入
                if (side === 'right') {
                    parts.push('<span class="diff-token diff-token-added">' + text + '</span>');
                }
                // 左侧对插入文本不显示
            }
        }
        return parts.join('');
    }

    function ensureSyncScroll(textarea, contentDiv) {
        if (!textarea || !contentDiv) return;
        if (contentDiv.__syncBound) return;
        textarea.addEventListener('scroll', function() {
            contentDiv.scrollTop = textarea.scrollTop;
            contentDiv.scrollLeft = textarea.scrollLeft;
        });
        contentDiv.__syncBound = true;
    }

    // 跨左右区域的滚动同步
    let crossSyncEnabled = true;
    let isSyncing = false;
    window.setCrossSyncEnabled = function(enabled){
        crossSyncEnabled = !!enabled;
    }
    function bindCrossSync() {
        const leftTA = document.getElementById('leftText');
        const rightTA = document.getElementById('rightText');
        const leftOverlay = document.getElementById('leftTextContent');
        const rightOverlay = document.getElementById('rightTextContent');
        if (!leftTA || !rightTA || !leftOverlay || !rightOverlay) return;

        function mirror(fromTA, toTA, fromOverlay, toOverlay) {
            fromTA.addEventListener('scroll', function(){
                if (!crossSyncEnabled) return;
                if (isSyncing) return;
                isSyncing = true;
                toTA.scrollTop = fromTA.scrollTop;
                toTA.scrollLeft = fromTA.scrollLeft;
                // 覆盖层也跟随
                fromOverlay.scrollTop = fromTA.scrollTop;
                fromOverlay.scrollLeft = fromTA.scrollLeft;
                toOverlay.scrollTop = toTA.scrollTop;
                toOverlay.scrollLeft = toTA.scrollLeft;
                isSyncing = false;
            });
        }

        if (!leftTA.__crossSyncBound) {
            mirror(leftTA, rightTA, leftOverlay, rightOverlay);
            leftTA.__crossSyncBound = true;
        }
        if (!rightTA.__crossSyncBound) {
            mirror(rightTA, leftTA, rightOverlay, leftOverlay);
            rightTA.__crossSyncBound = true;
        }
    }

    function simpleLineDiff(leftLines, rightLines) {
        var leftSet = new Map();
        for (var i = 0; i < leftLines.length; i++) {
            var arr = leftSet.get(leftLines[i]) || [];
            arr.push(i);
            leftSet.set(leftLines[i], arr);
        }
        var rightSet = new Map();
        for (var j = 0; j < rightLines.length; j++) {
            var arr2 = rightSet.get(rightLines[j]) || [];
            arr2.push(j);
            rightSet.set(rightLines[j], arr2);
        }
        var leftMarked = new Set();
        var rightMarked = new Set();
        for (var i1 = 0; i1 < leftLines.length; i1++) {
            if (!rightSet.has(leftLines[i1])) leftMarked.add(i1);
        }
        for (var j1 = 0; j1 < rightLines.length; j1++) {
            if (!leftSet.has(rightLines[j1])) rightMarked.add(j1);
        }
        var leftCount = leftMarked.size;
        var rightCount = rightMarked.size;
        var leftHTML = leftLines.map(function(l, idx){
            var s = escapeHtml(l);
            return leftMarked.has(idx) ? '<span class="diff-highlight diff-deleted">' + s + '</span>' : s;
        }).join('<br>');
        var rightHTML = rightLines.map(function(r, idx){
            var s = escapeHtml(r);
            return rightMarked.has(idx) ? '<span class="diff-highlight diff-added">' + s + '</span>' : s;
        }).join('<br>');
        return { leftHTML: leftHTML, rightHTML: rightHTML, leftCount: leftCount, rightCount: rightCount, diffCount: leftCount + rightCount };
    }
    
    // 计算文本位置
    window.calculateTextPosition = (textarea, position) => {
        var text = textarea.value;
        var lines = text.substring(0, position).split('\n');
        var line = lines.length - 1;
        var column = lines[lines.length - 1].length;
        
        // 获取文本区域的位置和样式
        var rect = textarea.getBoundingClientRect();
        var computedStyle = window.getComputedStyle(textarea);
        
        var lineHeight = parseInt(computedStyle.lineHeight) || 20;
        var paddingTop = parseInt(computedStyle.paddingTop) || 0;
        var paddingLeft = parseInt(computedStyle.paddingLeft) || 0;
        var fontSize = parseInt(computedStyle.fontSize) || 14;
        
        var x = paddingLeft + (column * fontSize * 0.6); // 近似字符宽度
        var y = paddingTop + (line * lineHeight);
        
        return { x: x, y: y, width: fontSize * 0.6, height: lineHeight };
    };
</script>

<style>
.text-diff-container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 24px;
}

/* 操作按钮区域 */
.action-buttons {
    display: flex;
    gap: 12px;
    margin-bottom: 24px;
    flex-wrap: wrap;
}

.action-buttons .btn {
    margin-bottom: 0;
}

/* 文本输入区域 */
.diff-inputs {
    display: flex;
    gap: 24px;
    margin-bottom: 20px;
}

.diff-section {
    flex: 1;
    display: flex;
    flex-direction: column;
}

.diff-section label {
    font-weight: 600;
    margin-bottom: 8px;
    color: #333;
}

/* 文本区域容器 */
.textarea-container {
    position: relative;
    display: flex;
    flex-direction: column;
}

.diff-textarea-wrapper {
    position: relative;
    min-height: 200px;
    border-radius: 8px;
    border: 2px solid #e1e5e9;
    background: #fff;
    overflow: hidden;
}

.diff-textarea-content {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 12px;
    font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
    font-size: 14px;
    line-height: 1.5;
    color: #333;
    pointer-events: none;
    z-index: 1;
    white-space: pre-wrap;
    word-wrap: break-word;
    overflow: hidden;
}

.diff-textarea {
    position: relative;
    width: 100%;
    min-height: 200px;
    font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
    font-size: 14px;
    line-height: 1.5;
    padding: 12px;
    border: none;
    background: transparent;
    resize: vertical;
    z-index: 2;
    color: #333;
    white-space: pre-wrap;
    word-wrap: break-word;
}

.diff-textarea:disabled {
    color: transparent;
    -webkit-text-fill-color: transparent; /* Safari/Chrome */
    caret-color: transparent;
}

.diff-textarea:focus {
    outline: none;
}

.diff-textarea-wrapper:focus-within {
    border-color: #007bff;
    box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}

/* Diff高亮样式 */
.diff-highlight {
    border-radius: 3px;
    padding: 1px 2px;
    margin: 0 1px;
}

.diff-added {
    background-color: #d4edda;
    color: #155724;
    border: 1px solid #c3e6cb;
}

.diff-deleted {
    background-color: #f8d7da;
    color: #721c24;
    border: 1px solid #f5c6cb;
}

.diff-modified {
    background-color: #fff3cd;
    color: #856404;
    border: 1px solid #ffeaa7;
}

/* 行内字符级标记 */
.diff-token {
    border-radius: 2px;
    padding: 0 1px;
}
.diff-token-added {
    background-color: #9be7ab; /* 更深的绿底 */
    color: #0e4d1e;
}
.diff-token-deleted {
    background-color: #f5a3ab; /* 更深的红底 */
    color: #5a0e16;
}

/* 差异统计信息 */
.diff-stats {
    display: flex;
    gap: 20px;
    margin-bottom: 24px;
    padding: 16px;
    background: #f8f9fa;
    border-radius: 8px;
    border-left: 4px solid #007bff;
}

.stats-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 80px;
}

.stat-label {
    font-size: 12px;
    color: #6c757d;
    margin-bottom: 4px;
}

.stat-value {
    font-size: 18px;
    font-weight: bold;
    color: #495057;
}

.stat-value.deleted {
    color: #dc3545;
}

.stat-value.added {
    color: #28a745;
}

/* 历史记录区域 */
.history-section {
    margin-top: 24px;
}

.history-section h3 {
    margin-bottom: 16px;
    color: #495057;
    font-size: 18px;
}

.history-list {
    max-height: 300px;
    overflow-y: auto;
    border: 1px solid #e1e5e9;
    border-radius: 8px;
    background: #fff;
}

.history-item {
    display: flex;
    align-items: center;
    padding: 12px 16px;
    border-bottom: 1px solid #f1f3f4;
    transition: background-color 0.2s ease;
}

.history-item:hover {
    background-color: #f8f9fa;
}

.history-item:last-child {
    border-bottom: none;
}

.history-time {
    font-size: 12px;
    color: #6c757d;
    min-width: 60px;
    margin-right: 12px;
}

.history-action {
    font-weight: 600;
    color: #495057;
    min-width: 100px;
    margin-right: 12px;
}

.history-details {
    color: #6c757d;
    font-size: 14px;
    flex: 1;
}

.no-history {
    padding: 24px;
    text-align: center;
    color: #6c757d;
    font-style: italic;
}

/* 按钮样式 */
.btn {
    padding: 8px 16px;
    border-radius: 6px;
    border: none;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    text-decoration: none;
    display: inline-block;
}

.btn:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.btn-primary {
    background-color: #007bff;
    color: white;
}

.btn-primary:hover:not(:disabled) {
    background-color: #0056b3;
}

.btn-success {
    background-color: #28a745;
    color: white;
}

.btn-success:hover:not(:disabled) {
    background-color: #1e7e34;
}

.btn-info {
    background-color: #17a2b8;
    color: white;
}

.btn-info:hover:not(:disabled) {
    background-color: #138496;
}

.btn-warning {
    background-color: #ffc107;
    color: #212529;
}

.btn-warning:hover:not(:disabled) {
    background-color: #e0a800;
}

/* 响应式设计 */
@@media (max-width: 768px) {
    .diff-inputs {
        flex-direction: column;
        gap: 16px;
    }
    
    .action-buttons {
        flex-direction: column;
    }
    
    .diff-stats {
        flex-direction: column;
        gap: 12px;
    }
    
    .stats-item {
        flex-direction: row;
        justify-content: space-between;
        min-width: auto;
    }
}
</style>
